using System;
using System.Xml;
using System.Collections.Generic;

namespace MIS.EN.WebDav
{
    /// <summary>
    /// A helper class intended to make it easy to retrieve property values from WebDAV responses.
    /// </summary>
    internal class WebDavXmlHelper
    {
        private XmlDocument document;
        private bool useWebDavReplication;
        private XmlNamespaceManager namespaceManager;
        private Dictionary<string, string> namespaces;
        private string collBlob = string.Empty;
        
        /// <summary>
        /// Public constructor which takes an <see cref="System.Xml.XmlDocument"/> instance 
        /// for the instance to query against later.
        /// </summary>
        /// <param name="document">An <see cref="System.Xml.XmlDocument"/> instance that 
        /// represents a WebDAV response</param>
        /// <param name="useWebDavReplication"></param>
        /// <param name="namespaces"></param>
        public WebDavXmlHelper(XmlDocument document, bool useWebDavReplication, 
            Dictionary<string, string> namespaces)
        {
            this.document = document;
            this.useWebDavReplication = useWebDavReplication;
            this.namespaces = namespaces;
            this.InitializeXmlNamespaceManager();
            if (this.useWebDavReplication)
            {
                this.RetrieveCollBlob();
            }
        }

        /// <summary>
        /// The <see cref="System.Xml.XmlNamespaceManager"/> for the 
        /// underlying <see cref="System.Xml.XmlDocument"/>.
        /// </summary>
        public XmlNamespaceManager NamespaceManager
        {
            get { return this.namespaceManager; }
        }

        /// <summary>
        /// Given a particular response node of the WebDAV response <see cref="System.Xml.XmlDocument"/>, 
        /// this will return a property value from the response node based on the property name.
        /// </summary>
        /// <param name="responseNode">An <see cref="System.Xml.XmlNode"/> representing a node in the document.</param>
        /// <param name="nameSpacePrefix">The XML namespace prefix of the property being queried.</param>
        /// <param name="propertyName">The name of the property being queried.</param>
        /// <returns>A <see cref="System.String"/> representing the value of the property.</returns>
        public string GetStringPropertyValueFromResponseNode(XmlNode responseNode, string nameSpacePrefix, string propertyName)
        {
            XmlNode node = this.GetPropertyNode(responseNode, nameSpacePrefix, propertyName);
            return this.GetNodeValue(node);
        }

        /// <summary>
        /// Given a particular response node of the WebDAV response <see cref="System.Xml.XmlDocument"/>, 
        /// this will return an integer property value from the response node based on the property name.
        /// </summary>
        /// <param name="responseNode">An <see cref="System.Xml.XmlNode"/> representing a node in the document.</param>
        /// <param name="nameSpacePrefix">The XML namespace prefix of the property being queried.</param>
        /// <param name="propertyName">The name of the property being queried.</param>
        /// <returns>An <see cref="System.Int32"/> representing the value of the property.</returns>
        public int GetInt32PropertyValueFromResponseNode(XmlNode responseNode, string nameSpacePrefix, string propertyName)
        {
            int nodeValue = 0;
            int.TryParse(this.GetStringPropertyValueFromResponseNode(responseNode, nameSpacePrefix, propertyName), out nodeValue);
            return nodeValue;
        }

        /// <summary>
        /// Given a particular response node of the WebDAV response <see cref="System.Xml.XmlDocument"/>, 
        /// this will return a double property value from the response node based on the property name.
        /// </summary>
        /// <param name="responseNode">An <see cref="System.Xml.XmlNode"/> representing a node in the document.</param>
        /// <param name="nameSpacePrefix">The XML namespace prefix of the property being queried.</param>
        /// <param name="propertyName">The name of the property being queried.</param>
        /// <returns>A <see cref="System.Double"/> representing the value of the property.</returns>
        public double GetDoublePropertyValueFromResponseNode(XmlNode responseNode, string nameSpacePrefix, string propertyName)
        {
            double nodeValue = 0.0;
            double.TryParse(this.GetStringPropertyValueFromResponseNode(responseNode, nameSpacePrefix, propertyName), out nodeValue);
            return nodeValue;
        }

        /// <summary>
        /// Given a particular response node of the WebDAV response <see cref="System.Xml.XmlDocument"/>, 
        /// this will return a boolean property value from the response node based on the property name.
        /// </summary>
        /// <param name="responseNode">An <see cref="System.Xml.XmlNode"/> representing a node in the document.</param>
        /// <param name="nameSpacePrefix">The XML namespace prefix of the property being queried.</param>
        /// <param name="propertyName">The name of the property being queried.</param>
        /// <returns>A <see cref="System.Boolean"/> representing the value of the property.</returns>
        public bool GetBoolPropertyValueFromResponseNode(XmlNode responseNode, string nameSpacePrefix, string propertyName)
        {
            return (this.GetStringPropertyValueFromResponseNode(responseNode, nameSpacePrefix, propertyName) == "1" ? true : false);
        }

        /// <summary>
        /// Given a particular response node of the WebDAV response <see cref="System.Xml.XmlDocument"/>, 
        /// this will return a DateTime property value from the response node based on the property name.
        /// </summary>
        /// <param name="responseNode">An <see cref="System.Xml.XmlNode"/> representing a node in the document.</param>
        /// <param name="nameSpacePrefix">The XML namespace prefix of the property being queried.</param>
        /// <param name="propertyName">The name of the property being queried.</param>
        /// <returns>A <see cref="System.DateTime"/> representing the value of the property.</returns>
        public DateTime GetDateTimePropertyValueFromResponseNode(XmlNode responseNode, string nameSpacePrefix, string propertyName)
        {
            DateTime nodeValue;
            DateTime.TryParse(this.GetStringPropertyValueFromResponseNode(responseNode, nameSpacePrefix, propertyName), out nodeValue);
            return nodeValue;
        }

        /// <summary>
        /// Given a particular response node of the WebDAV response <see cref="System.Xml.XmlDocument"/>, 
        /// this will return an enum property value from the response node based on the property name and 
        /// the enum type passed in.
        /// </summary>
        /// <param name="responseNode">An <see cref="System.Xml.XmlNode"/> representing a node in the document.</param>
        /// <param name="nameSpacePrefix">The XML namespace prefix of the property being queried.</param>
        /// <param name="propertyName">The name of the property being queried.</param>
        /// <param name="type">The type name of the enum.</param>
        /// <returns>An object representing the parsed enum value of the property.</returns>
        public object GetEnumPropertyValueFromResponseNode(XmlNode responseNode, string nameSpacePrefix, string propertyName, string type)
        {
            object parsedEnumValue = null;
            string rawEnumValue = this.GetStringPropertyValueFromResponseNode(responseNode, nameSpacePrefix, propertyName);
            if (rawEnumValue.Length > 0)
            {
                parsedEnumValue = Enum.Parse(Type.GetType(type), rawEnumValue, true);
            }
            return parsedEnumValue;
        }

        /// <summary>
        /// Given a particular response node of the WebDAV response <see cref="System.Xml.XmlDocument"/>, 
        /// this will return the integer value of an enum property value from the response node based 
        /// on the property name and the enum type passed in.
        /// </summary>
        /// <param name="responseNode">An <see cref="System.Xml.XmlNode"/> representing a node in the document.</param>
        /// <param name="nameSpacePrefix">The XML namespace prefix of the property being queried.</param>
        /// <param name="propertyName">The name of the property being queried.</param>
        /// <param name="type">The type name of the enum.</param>
        /// <returns>An object representing the parsed enum value of the property.</returns>
        public object GetEnumInt32PropertyValueFromResponseNode(XmlNode responseNode, string nameSpacePrefix, string propertyName, string type)
        {
            return Enum.ToObject(Type.GetType(type), this.GetInt32PropertyValueFromResponseNode(responseNode, nameSpacePrefix, propertyName));
        }

        /// <summary>
        /// Gets an instance of a WebDAV property node, based on the namespaced name of the property.
        /// </summary>
        /// <param name="responseNode"></param>
        /// <param name="nameSpacePrefix"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        private XmlNode GetPropertyNode(XmlNode responseNode, string nameSpacePrefix, string propertyName)
        {
            return responseNode.SelectSingleNode("descendant::a:propstat/a:prop/" + nameSpacePrefix + ":"
                + propertyName, this.namespaceManager);
        }

        /// <summary>
        /// Gets the value of a node, which is the InnerXml of the node.
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private string GetNodeValue(XmlNode node)
        {
            string nodeValue = string.Empty;
            if (node != null)
            {
                if (node.InnerXml != null)
                {
                    nodeValue = node.InnerXml;
                }
            }
            return nodeValue;
        }

        /// <summary>
        /// Given a particular response node of the WebDAV response <see cref="System.Xml.XmlDocument"/>, 
        /// this will return the Exchange href property value.
        /// </summary>
        /// <param name="responseNode">An <see cref="System.Xml.XmlNode"/> representing a node in the document.</param>
        /// <returns>A <see cref="System.String"/> representing the href value of the Exchange item.</returns>
        public string GetHrefPropertyFromResponseNode(XmlNode responseNode)
        {
            XmlNode node = responseNode.SelectSingleNode("descendant::a:href", this.namespaceManager);
            return this.GetNodeValue(node);
        }

        public string CollBlob
        {
            get { return this.collBlob; }
        }

        private void RetrieveCollBlob()
        {
            XmlNode node = this.document.SelectSingleNode("//d:repl/d:collblob", this.namespaceManager);
            this.collBlob = this.GetNodeValue(node);
        }

        private void InitializeXmlNamespaceManager()
        {
            this.namespaceManager = new XmlNamespaceManager(this.document.NameTable);
            foreach (string key in this.namespaces.Keys)
            {
                this.namespaceManager.AddNamespace(key, this.namespaces[key]);
            }
        }
    }
}
